package mapx

import (
	"gitee.com/zhongguo168a/gocodes/datax/convertx"
	"sort"
	"strconv"
	"strings"
	"sync"
)

var (
	mu sync.RWMutex
)

func Keys(t map[string]interface{}) (r []string) {

	for key := range t {
		r = append(r, key)
	}

	sort.Strings(r)
	return
}

func Set(t map[string]interface{}, key string, val interface{}) {
	old := t[key]
	if old == nil {
		switch data := val.(type) {
		case map[string]interface{}:
			m := map[string]interface{}{}
			t[key] = m
			for dk, dv := range data {
				m[dk] = dv
			}
		default:
			t[key] = val
		}
	} else {
		switch data := old.(type) {
		case map[string]interface{}:
			if val == nil {
				t[key] = val
			} else {
				m := val.(map[string]interface{})
				for dk, dv := range m {
					data[dk] = dv
				}
			}
		default:
			t[key] = val
		}
	}
}

func HasKeys(t map[string]interface{}, keys ...string) bool {
	var has bool
	var m interface{}
	cur := t

	for i, k := range keys {
		m, has = cur[k]
		if has == false {
			return false
		}

		cur, has = m.(map[string]interface{})
		if has == false && i == len(keys)-1 {
			return true
		}
	}

	return true
}

func Has(t map[string]interface{}, key string) bool {
	_, ok := t[key]
	return ok
}

func Interface(t map[string]interface{}, key string) (r interface{}) {
	if t == nil {
		return
	}

	ir, ok := t[key]
	if ok == false {
		return
	}

	r = ir
	return
}

func String(t map[string]interface{}, key string) (r string) {
	if t == nil {
		return
	}

	ir, ok := t[key]
	if ok == false {
		return
	}

	r, ok = ir.(string)
	if ok == false {
		return
	}
	return
}

func StringDefault(t map[string]interface{}, key string, dflt string) (r string) {
	if t == nil {
		return dflt
	}

	ir, ok := t[key]
	if ok == false {
		return dflt
	}

	r, ok = ir.(string)
	if ok == false {
		return
	}
	return
}

func Int(t map[string]interface{}, key string) (r int) {
	if t == nil {
		return
	}

	ir, ok := t[key]
	if ok == false {
		return
	}

	r = int(convertx.AnyToInt64(ir))
	return
}

func IntDefault(t map[string]interface{}, key string, dflt int) (r int) {
	if t == nil {
		return dflt
	}

	ir, ok := t[key]
	if ok == false {
		return dflt
	}

	r = int(convertx.AnyToInt64(ir))
	return
}

func Float64(t map[string]interface{}, key string) (r float64) {
	if t == nil {
		return
	}

	ir, ok := t[key]
	if ok == false {
		return
	}

	r = convertx.AnyToFloat64(ir)
	return
}

func Float64Default(t map[string]interface{}, key string, dflt float64) (r float64) {
	if t == nil {
		return dflt
	}

	ir, ok := t[key]
	if ok == false {
		return dflt
	}

	r = convertx.AnyToFloat64(ir)
	return
}

func Bool(t map[string]interface{}, key string) (r bool) {
	if t == nil {
		return
	}

	ir, ok := t[key]
	if ok == false {
		return
	}
	r = convertx.AnyToBool(ir)
	return
}

func BoolDefault(t map[string]interface{}, key string, dflt bool) (r bool) {
	if t == nil {
		return dflt
	}

	ir, ok := t[key]
	if ok == false {
		return dflt
	}
	r = convertx.AnyToBool(ir)
	return
}

var mutex = sync.RWMutex{}

func Map(t map[string]interface{}, key string) (r map[string]interface{}) {
	if t == nil {
		return
	}

	mutex.RLock()
	ir, ok := t[key]
	mutex.RUnlock()
	if ok == false {
		mutex.Lock()
		r = map[string]interface{}{}
		t[key] = r
		mutex.Unlock()
		return
	}

	if ir == nil {
		return
	}

	r = ir.(map[string]interface{})
	return
}

// path = "/a/b/c"
func DeleteByPath(t map[string]interface{}, path string) {
	if t == nil {
		return
	}
	if string(path[0]) != "/" {
		panic("路径的首字符必须为/")
	}
	path = path[1:]
	arr := strings.Split(path, "/")
	curmap := t
	curkey := ""
	for {
		if len(arr) == 1 {
			break
		}
		curkey = arr[0]
		arr = arr[1:]

		ir, ok := curmap[curkey]
		if ok == false {
			return
		}
		curmap = ir.(map[string]interface{})
	}

	delete(curmap, arr[0])
}

func CreateByPath(path string) map[string]interface{} {
	if string(path[0]) != "/" {
		panic("路径的首字符必须为/")
	}

	m := map[string]interface{}{}
	SetByPath(m, path, nil)
	return m
}

// path = "/a/b/c"
func SetByPath(t map[string]interface{}, path string, value interface{}) {
	if t == nil {
		return
	}
	if string(path[0]) != "/" {
		panic("路径的首字符必须为/")
	}

	if path == "/" {
		m, ok := value.(map[string]interface{})
		if !ok {
			panic("value must map")
		}
		for key, val := range m {
			t[key] = val
		}
		return
	}
	path = path[1:]
	arr := strings.Split(path, "/")
	curmap := t
	curkey := ""
	for {
		if len(arr) == 1 {
			break
		}
		curkey = arr[0]
		arr = arr[1:]

		ir, ok := curmap[curkey]
		if ok == false || ir == nil {
			tempr := map[string]interface{}{}
			curmap[curkey] = tempr
			ir = tempr
		}
		r := ir.(map[string]interface{})
		if r == nil {
			tempr := map[string]interface{}{}
			curmap[curkey] = tempr
			r = tempr
		}
		curmap = r
	}

	curmap[arr[0]] = value
}

// path = "/a/b/c"
func GetByPath(t map[string]interface{}, path string) (value interface{}, has bool) {
	if t == nil {
		return nil, false
	}
	if string(path[0]) != "/" {
		panic("路径的首字符必须为/")
	}
	path = path[1:]
	arr := strings.Split(path, "/")
	field := arr[len(arr)-1]
	arr = arr[:len(arr)-1]
	var icurmap interface{} = t
	for {
		if len(arr) == 0 {
			break
		}

		switch curmap := icurmap.(type) {
		case map[string]interface{}:
			curkey := arr[0]
			ir, ok := curmap[curkey]
			if ok == false {
				return nil, false
			}
			icurmap = ir
		case []interface{}:
			curkey := convertx.AnyToInt(arr[0])
			if curkey >= len(curmap) {
				return nil, false
			}
			icurmap = curmap[curkey]
		}
		arr = arr[1:]
	}

	has = true
	switch curmap := icurmap.(type) {
	case map[string]interface{}:
		value = curmap[field]
	case []interface{}:
		value = curmap[convertx.AnyToInt(field)]
	}
	return
}

// 目标map是否有路径
func HasByPath(t map[string]interface{}, path string) bool {
	if t == nil {
		return false
	}
	if string(path[0]) != "/" {
		panic("路径的首字符必须为/")
	}
	path = path[1:]
	arr := strings.Split(path, "/")

	curmap := t
	curkey := ""
	for {
		if len(arr) == 0 {
			return true
		}
		curkey = arr[0]
		arr = arr[1:]

		ir, ok := curmap[curkey]
		if ok == false || ir == nil {
			return false
		}
		curmap = ir.(map[string]interface{})
	}
	return true
}

// 如果map路径不存在, 会创建map
// path = "/a/b/c/.."
// 如果path = "/" 直接返回t
func MapByPath(t map[string]interface{}, path string, autoCreate bool) (r map[string]interface{}) {
	if t == nil {
		return
	}
	if string(path[0]) != "/" {
		panic("路径的首字符必须为/")
	}

	if path == "/" {
		return t
	}

	path = path[1:]
	arr := strings.Split(path, "/")

	var icurmap interface{} = t
	for {
		if len(arr) == 0 {
			return
		}

		switch curmap := icurmap.(type) {
		case map[string]interface{}:
			curkey := arr[0]
			ir, ok := curmap[curkey]
			if ok == false || ir == nil {
				if !autoCreate {
					return nil
				}
				r = map[string]interface{}{}
				ir = r
				curmap[curkey] = r
			}
			r = ir.(map[string]interface{})
			if r == nil {
				if !autoCreate {
					return r
				}
				r = map[string]interface{}{}
				curmap[curkey] = r
			}
			icurmap = r
		case []interface{}:

		}
		arr = arr[1:]

	}
	return
}

// 获取map的array, 如果不存在就创建一个
func Array(t map[string]interface{}, key string) (r []interface{}) {
	if t == nil {
		return
	}

	ir, ok := t[key]
	if ok == false {
		r = []interface{}{}
		t[key] = r
		return
	}

	if ir == nil {
		return
	}

	r = ir.([]interface{})

	return
}

func Flat(t map[string]interface{}, detail bool) map[string]interface{} {
	if t == nil {
		return nil
	}

	result := map[string]interface{}{}
	flat(t, detail, result, "")
	return result
}
func flat(iValue interface{}, detail bool, result map[string]interface{}, path string) {
	switch v := iValue.(type) {
	case map[string]interface{}:
		if v != nil {
			if detail {
				if path == "" {
					result["/"] = len(v)
				} else {
					result[path] = len(v)
				}
			}
			var keys []string
			for key, val := range v {
				keys = append(keys, key)
				flat(val, detail, result, path+"/"+key)
			}
			result[path+"/keys"] = keys
		} else {
			if detail {
				result[path] = nil
			}
		}

	case []interface{}:
		if detail {
			if path == "" {
				result["/"] = len(v)
			} else {
				result[path] = len(v)
			}
		}
		for i, val := range v {
			flat(val, detail, result, path+"/"+strconv.Itoa(i))
		}
	default:
		result[path] = iValue
	}
}

type IFromMap interface {
	FromMap(m map[string]interface{}, args ...interface{})
}

func StructFromValue(st IFromMap, val interface{}, args ...interface{}) IFromMap {
	m, ok := val.(map[string]interface{})
	if ok == true {
		st.FromMap(m, args...)
	}
	return st
}

var NewMap = map[string]func(args ...interface{}) interface{}{}

func StructFromByType(val interface{}, args ...interface{}) (r interface{}) {
	bm, ok := val.(map[string]interface{})
	if ok && Has(bm, "_type") {
		typ := String(bm, "_type")
		newFunc, has := NewMap[typ]
		if has == false {
			r = val
		} else {
			r = newFunc(args...)
			fm := r.(IFromMap)
			fm.FromMap(bm, args...)
		}
	} else {
		r = val
	}
	return
}

func StructFromByInterface(target, val interface{}, args ...interface{}) (r interface{}) {
	switch v := val.(type) {
	case map[string]interface{}:
		if t, ok := target.(map[string]interface{}); ok == false {
			t = map[string]interface{}{}
			target = t
			Copy(v, t)
		} else {
			Copy(v, t)
		}
	default:
		target = val
	}

	return target
}

type IToMap interface {
	ToMap(m map[string]interface{}, args ...interface{})
}
type IToMapAll interface {
	ToMapAll(m map[string]interface{}, args ...interface{})
}

type IStructName interface {
	StructName() string
}

func StructToValueNotNull(st IToMap, args ...interface{}) (r interface{}) {
	if st != nil {
		a := map[string]interface{}{}
		if stname, ok := st.(IStructName); ok {
			a["_type"] = stname.StructName()
		}
		st.ToMap(a, args...)
		r = a
	}
	return
}

func StructToValue(st IToMap, val interface{}, name string, args ...interface{}) {
	m, ok := val.(map[string]interface{})
	if ok == true {
		if st != nil {
			a := map[string]interface{}{}
			m[name] = a
			st.ToMap(a, args...)
		}
	}
	return
}

func StructToByType(val interface{}, args ...interface{}) (r interface{}) {

	toval, ok := val.(IToMap)
	if ok {
		m := map[string]interface{}{}
		toval.ToMap(m, args...)
		if stname, ok := toval.(IStructName); ok {
			m["_type"] = stname.StructName()
		}
		r = m
	} else {
		switch v := val.(type) {
		case []interface{}:
			r = copyArray(v, []interface{}{}, true)
		case map[string]interface{}:
			m := map[string]interface{}{}
			r = copyAny(v, m, true)
		default:
			r = v
		}
	}

	return
}

func ArrayByMap(val interface{}, handler func(length int, keys []int, items []interface{})) (r []interface{}) {
	var keys []int
	var items []interface{}
	switch b := val.(type) {
	case map[string]interface{}:
		var max int64 = -1
		for key, item := range b {
			k := convertx.AnyToInt64(key)
			keys = append(keys, int(k))
			items = append(items, item)

			if k > max {
				max = k
			}
		}
		handler(int(max)+1, keys, items)
	case []interface{}:
		for i, item := range b {
			keys = append(keys, int(i))
			items = append(items, item)
		}
		handler(len(b), keys, items)
	}
	return
}
